package org.example.auth.config.oauth;

import cn.hutool.core.util.ObjectUtil;
import org.example.auth.domain.LoginUserDetails;
import org.example.common.core.constants.Constants;
import org.example.common.core.constants.TenantConstants;
import org.example.common.core.domain.model.LoginModel;
import org.example.common.core.domain.model.LoginUser;
import org.example.common.core.enums.DeviceType;
import org.example.common.core.enums.GrantUser;
import org.example.common.core.utils.StringUtils;
import org.example.common.redis.utils.LoginHelper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 令牌相关配置类
 */
@Configuration
public class JwtTokenStoreConfiguration {

    /**
     * 不同的系统用不同的jwtKey；不推荐共用一样的
     * <p>
     * HMAC key, default: IH6S2dhCEMwGr7uE4fBakSuDh9SoIrRa
     * alg: HMACSHA256
     */
    @Value("${sos.token.store.jwt.key:IH6S2dhCEMwGr7uE4fBakSuDh9SoIrRa}")
    private String jwtKey;
    /**
     * 是否重复使用已经有的 refresh_token 直到过期，默认true
     */
    @Value("${sos.reuse.refresh-token:true}")
    private boolean reuseRefreshToken;

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(jwtAccessTokenConverter());
    }


    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenEnhancer();
        jwtAccessTokenConverter.setSigningKey(this.jwtKey);
        return jwtAccessTokenConverter;
    }
    /**
     * 令牌服务的配置
     * 令牌的存储策略需要配置，还需要配置令牌的服务AuthorizationServerTokenServices用来创建、获取、刷新令牌
     * @return
     */
    @Bean
    public DefaultTokenServices tokenServices(TokenStore tokenStore, ClientDetailsService clientDetailsService) {
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setTokenStore(tokenStore);
        tokenServices.setClientDetailsService(clientDetailsService);
        //support refresh token
        tokenServices.setSupportRefreshToken(true);
//        //配置Jwt内容增强器
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> delegates = new ArrayList<>();
        delegates.add(jwtAccessTokenConverter());
        tokenEnhancerChain.setTokenEnhancers(delegates);
        tokenServices.setTokenEnhancer(tokenEnhancerChain);
        tokenServices.setReuseRefreshToken(this.reuseRefreshToken);
        tokenServices.setAccessTokenValiditySeconds(Constants.ACCESS_TOKEN_VALIDITY_SECONDS);
        tokenServices.setRefreshTokenValiditySeconds(Constants.REFRESH_TOKEN_VALIDITY_SECONDS);
        return tokenServices;
    }




    /**
     * JWT令牌增强，继承JwtAccessTokenConverter
     * 将业务所需的额外信息放入令牌中，这样下游微服务就能解析令牌获取
     */
    public static class JwtAccessTokenEnhancer extends JwtAccessTokenConverter {
        /**
         * 重写enhance方法，在其中扩展
         */
        @Override
        public org.springframework.security.oauth2.common.OAuth2AccessToken enhance(org.springframework.security.oauth2.common.OAuth2AccessToken accessToken,
                                                                                    OAuth2Authentication authentication) {
            LoginUserDetails  userDetails=null;
          if(ObjectUtil.isNotNull(authentication.getUserAuthentication())){
                userDetails= (LoginUserDetails)authentication.getUserAuthentication().getPrincipal();
           }else{
                String ClientId= authentication.getOAuth2Request().getClientId();
                LoginUser loginUser=new LoginUser();
                loginUser.setTenantId(TenantConstants.DEFAULT_TENANT_ID);
                String userId= StringUtils.padl(StringUtils.englishToNumber(ClientId),19,'0');
                loginUser.setUserId(Long.valueOf(userId));
                loginUser.setUsername(ClientId);
                loginUser.setUserType("client_user");
                LoginModel loginModel=LoginModel.create();
                loginModel.setTimeout(accessToken.getExpiresIn());
                loginModel.setToken(StringUtils.getUUID());
                loginModel.setDevice(DeviceType.UNDEFINED.getCode());
                loginModel.setGrantUser(GrantUser.CLIENT.getCode());
                LoginHelper.login(loginUser,loginModel);
                userDetails= new LoginUserDetails(loginUser.getUsername(),
                        loginUser.getPassword(),
                        AuthorityUtils.createAuthorityList());
                userDetails.setGrantUser(loginModel.getGrantUser());
                userDetails.setDeviceType(loginModel.getDevice());
                userDetails.setToken(loginModel.getToken());
                userDetails.setTenantId(loginUser.getTenantId());
            }
            Map<String, Object> info = new HashMap<>();
            //我们需要增强的内容
            info.put(Constants.GRANT_USER, userDetails.getGrantUser());
            info.put(Constants.DEVICE_TYPE, userDetails.getDeviceType());
            info.put(Constants.UUID, userDetails.getToken());
            info.put(Constants.TENANT_ID, userDetails.getTenantId());
            ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info);
            return super.enhance(accessToken, authentication);
        }
    }
}
